今天比較早開始寫文章,既然沒有上班,就可以好好的深入研究一些 Laravel 的用法,看看自己有沒有什麼地方寫爛需要改進的。
今天來講 Laravel 的 Middleware,我們在 Day18 的 JWT 實作也有用到 Middleware 進行權限的判斷,以防止沒有 token 認證的請求。
public function __construct()
{
$this->middleware('auth:api', ['except' => ['login']]);
}
事實上 Middleware 主要就是協助我們在進行 HTTP 請求時檢查這個請求來源是否符合我們在裡面設定的規則,可以進行許多認證的工作,不用再寫在每一個 Controller 的 function 內了,Laravel 預設也放了一些 Middleware 進去,大多是認證相關或是 CSRF 的保護機制。
透過 Artisan 建立一支 Middleware
php artisan make:middleware CheckRoles
我們來建立一支判斷使用者權限的 Middleware,首先除了原先的 users
表以外,也要先建立一張資料表紀錄目前這個系統的所有權限名稱。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateRolesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('roles', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name', 100);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('roles');
}
}
使用者可能會有不同的權限,而每個權限下面也會有不同的使用者,因此權限對使用者是多對多的關係,所以在 users
跟 roles
表外,也需要一張對應表 user_roles
紀錄兩張表多對多的關係。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUserRolesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('user_roles', function (Blueprint $table) {
$table->unsignedBigInteger('user_id');
$table->unsignedBigInteger('role_id');
$table->index(["user_id"], 'fk_users_has_roles_users_idx');
$table->index(["role_id"], 'fk_users_has_roles_roles_idx');
$table->foreign('user_id', 'fk_users_has_roles_users_idx')
->references('id')->on('users');
$table->foreign('role_id', 'fk_users_has_roles_roles_idx')
->references('id')->on('roles');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('user_roles');
}
}
php artisan migrate
以上設置在資料庫的關聯中會長這樣。
進入 app/Models/User.php
內新增以下程式碼
public function roles()
{
return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'role_id');
}
return $this->belongsToMany('對應的 Model', '中間的對應表', '外鍵 1', '外鍵 2');
以上是除了在資料庫設定關聯外,也利用 Laravel 建立兩個 Model 多對多的關聯,可以讓你直接操作 Model,這部分我在之前建立資料庫的關聯忘記提及,之後再找時間把這塊補上 TAT。
建資料內容這邊不多提及,可利用 Laravel Seeder 的方法或是直接進入資料庫內進行操作都可以,我們預設的資料如下圖。
users
roles
user_roles
我們先到 routes/api
設定需要這個 Middleware 檢查的 Controller
Route::group([
// role:admin 代表你要在 Middleware 中設置變數 $role = admin
'middleware' => ['auth:api', 'role:admin'],
], function () {
Route::post('roles', [RolesController::class, 'store']);
});
接下來就到 app/Http/CheckRoles
內的 function handle 寫入這個權限的規則。
public function handle($request, Closure $next, $role)
{
// 透過 JWT 取得你的身份
$user = Auth::user();
// 從資料庫拉出你的權限
$system_roles = $user->roles;
switch ($role) {
case 'admin':
foreach ($system_roles as $system_role) {
if($system_role->name == "admin"){
// $next($request) 就是讓你的請求可以通過的意思
return $next($request);
}
}
return response()->json([
'message' => '您沒有權限進行此動作'
], 403);
}
如此一來,Middleware 可以在前端 POST /api/roles 時進行權限檢查,若是不符合你所設定的權限,則會先行 return 403 狀態,也完成了透過後端檢查權限的功能囉!